﻿using System;
using System.Collections.Generic;
using Caffeine.Core;
using Caffeine.Examples.Contacts.Repository;
using Caffeine.Wpf;
using Microsoft.Practices.Unity;
using System.Runtime.Serialization;

namespace Caffeine.Examples.Contacts.ViewModel
{
    [DataContract]
    public class AddressViewModel : ViewModelBase
    {
        #region State

        private Boolean _showCommitChangesCommand;
        private Boolean _showUndoChangesCommand;
        private Boolean _showRemoveFromContactCommand;
        private Boolean _showCancelAddToContactCommand;
        private Boolean _showCommitRemoveFromContactCommand;
        private Boolean _showUndoRemoveFromContactCommand;
        private readonly IUnityContainer _container;
        private readonly ActionCommand _save;
        private readonly ActionCommand _undoChanges;
        private readonly ActionCommand _cancelAddToContact;
        private readonly ActionCommand _removeFromContact;
        private readonly ActionCommand _removeFromContactCommit;
        private readonly ActionCommand _undoRemoveFromContact;
        private readonly ViewModelProperty<String> _streetAddress;
        private readonly ViewModelProperty<String> _zipCode;
        private readonly IContactRepository _repository;
        private static readonly Func<String, ViewModelProperty<String>, List<ValidationError>, Boolean> StreetAddressValidationMethod;
        private static readonly Func<String, ViewModelProperty<String>, List<ValidationError>, Boolean> ZipCodeValidationMethod;

        #endregion

        #region Constructors

        static AddressViewModel()
        {
            StreetAddressValidationMethod = ValidationHelper.GetStringValidationMethod
            (
                false,
                true,
                2,
                20,
                @"^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$",
                "Street address must be at least {0} characters or more",
                "Street address cannot be more than {0} characters long",
                "Must be a valid email address"
            );

            ZipCodeValidationMethod = ValidationHelper.GetStringValidationMethod
            (
                false,
                true,
                5,
                5,
                String.Empty,
                "Zip code must be {0} characters long",
                "Zip code must be {0} characters long",
                String.Empty
            );
        }

        public AddressViewModel()
        {
        }

        public AddressViewModel(IUnityContainer container)
        {
            this.StartDeferringChangeNotifications();
            this.StopDeferringChangeNotifications(false);
            this.StreetAddress.StartDeferringChangeNotifications();
            this.StreetAddress.StopDeferringChangeNotifications();
            this.ZipCode.StartDeferringChangeNotifications();
            this.ZipCode.StopDeferringChangeNotifications(false);
            this.ZipCode.History.ObserveChange("");
            this.ZipCode.History.StartDeferringChangeNotifications();
            this.ZipCode.History.StopDeferringChangeNotifications();
            this.ZipCode.History.IsObservingChanges = true;
            this.ZipCode.Value = "This is my new zip code";
            this.ZipCode.History.ObserveChange("a");
            this.ZipCode.History.ObserveChange("al");
            this.ZipCode.History.ObserveChange("alo");
            this.ZipCode.History.IsObservingChanges = false;
            this.SetPropertyChangeCounterForGraph(new PropertyChangeCounter());
            this.PropertyChangeCounter = new PropertyChangeCounter();
            this.EnableHistoryTracking();
            this.DisableHistoryTracking();
            this.EnableHistoryTrackingForGraph();
            this.DisableHistoryTrackingForGraph();
            this.StartDeferringChangeNotifications();
            this.StartDeferringChangeNotificationsForGraph();
            this.StopDeferringChangeNotifications();
            this.StopDeferringChangeNotificationsForGraph();
            this.StopDeferringChangeNotificationsForGraph(false);
            this.EnableHistoryTracking();
            this.EnableHistoryTrackingForGraph();
            this.DisableHistoryTrackingForGraph();
            this.StreetAddress.History = null;
            this.ReleaseObservers();
            this.ReleaseObserversForGraph();

            AddressViewModel viewModel = ViewModelFactory.Default.CreateViewModel<AddressViewModel>();
            

            this.PropertyChangeCounter.OnChangeCountChanged = delegate(UInt64 changeCount)
            {
                if (this.CheckCanMovePrevious())
                {
                    //Allow move next button to be invoked
                }

                //Allow move previous button to be invoked
            };

            this.MoveNext();
            this.MovePrevious();

            this.MoveNextForGraph();

            if (this.CheckCanMoveNext())
            {
            }

            if (this.CheckCanMovePreviousForGraph())
            {
            }

            this.MovePreviousForGraph();


            this.StartDeferringChangeNotifications();
            //Re-hydration logic here
            this.StopDeferringChangeNotifications(false);

            _container = container;
            _repository = Container.Resolve<IContactRepository>();
            _streetAddress = RegisterStringProperty("StreetAddress");
            _zipCode = RegisterStringProperty("ZipCode");

            StreetAddress.ValidationMethod = StreetAddressValidationMethod;
            ZipCode.ValidationMethod = ZipCodeValidationMethod;

            _save = new ActionCommand(OnSave);
            _undoChanges = new ActionCommand(OnUndoChanges);
            _removeFromContact = new ActionCommand(OnRemoveFromContact);
            _removeFromContactCommit = new ActionCommand(OnRemoveFromContactCommit);
            _undoRemoveFromContact = new ActionCommand(OnUndoRemoveFromContact);
            _cancelAddToContact = new ActionCommand(OnCancelAddToContact);

            StreetAddress.EnableHistory();
            ZipCode.EnableHistory();
            ZipCode.DisableHistory();
        }

        #endregion

        #region Public

        public IUnityContainer Container
        {
            get
            {
                return _container;
            }
        }
       
        public IContactRepository Repository
        {
            get
            {
                return _repository;
            }
        }

        public ActionCommand Save
        {
            get
            {
                return _save;
            }
        }

        public ActionCommand UndoChanges
        {
            get
            {
                return _undoChanges;
            }
        }

        public ActionCommand CancelAddToContact
        {
            get
            {
                return _cancelAddToContact;
            }
        }

        public ActionCommand RemoveFromContact
        {
            get
            {
                return _removeFromContact;
            }
        }

        public ActionCommand RemoveFromContactCommit
        {
            get
            {
                return _removeFromContactCommit;
            }
        }

        public ActionCommand UndoRemoveFromContact
        {
            get
            {
                return _undoRemoveFromContact;
            }
        }

        public Boolean ShowCommitChangesCommand
        {
            get
            {
                return _showCommitChangesCommand;
            }
            private set
            {
                if (value != _showCommitChangesCommand)
                {
                    _showCommitChangesCommand = value;
                    RaisePropertyChanged<AddressViewModel>(vm => vm.ShowCommitChangesCommand);
                }
            }
        }

        public Boolean ShowUndoChangesCommand
        {
            get
            {
                return _showUndoChangesCommand;
            }
            private set
            {
                if (value != _showUndoChangesCommand)
                {
                    _showUndoChangesCommand = value;
                    RaisePropertyChanged<AddressViewModel>(vm => vm.ShowUndoChangesCommand);
                }
            }
        }

        public Boolean ShowRemoveFromContactCommand
        {
            get
            {
                return _showRemoveFromContactCommand;
            }
            private set
            {
                if (value != _showRemoveFromContactCommand)
                {
                    _showRemoveFromContactCommand = value;
                    RaisePropertyChanged<AddressViewModel>(vm => vm.ShowRemoveFromContactCommand);
                }
            }
        }

        public Boolean ShowCancelAddToContactCommand
        {
            get
            {
                return _showCancelAddToContactCommand;
            }
            private set
            {
                if (value != _showCancelAddToContactCommand)
                {
                    _showCancelAddToContactCommand = value;
                    RaisePropertyChanged<AddressViewModel>(vm => vm.ShowCancelAddToContactCommand);
                }
            }
        }

        public Boolean ShowCommitRemoveFromContactCommand
        {
            get
            {
                return _showCommitRemoveFromContactCommand;
            }
            private set
            {
                if (value != _showCommitRemoveFromContactCommand)
                {
                    _showCommitRemoveFromContactCommand = value;
                    RaisePropertyChanged<AddressViewModel>(vm => vm.ShowCommitRemoveFromContactCommand);
                }
            }
        }

        public Boolean ShowUndoRemoveFromContactCommand
        {
            get
            {
                return _showUndoRemoveFromContactCommand;
            }
            private set
            {
                if (value != _showUndoRemoveFromContactCommand)
                {
                    _showUndoRemoveFromContactCommand = value;
                    RaisePropertyChanged<AddressViewModel>(vm => vm.ShowUndoRemoveFromContactCommand);
                }
            }
        }

        public ViewModelProperty<String> StreetAddress
        {
            get
            {
                return _streetAddress;
            }
        }

        public ViewModelProperty<String> ZipCode
        {
            get
            {
                return _zipCode;
            }
        }

        public void ConfigureCommandVisibility()
        {
            ShowCommitChangesCommand = (IsCommittedToCollection || IsAddedToCollection) &&
                                       ((IsDirty || IsDirtyWithin) && !IsInvalid) &&
                                       Parent.IsCommittedToCollection;
            ShowUndoChangesCommand = (IsCommittedToCollection || IsAddedToCollection) && (IsDirty || IsDirtyWithin);
            ShowRemoveFromContactCommand = IsCommittedToCollection && !(IsDirty || IsDirtyWithin);
            ShowCancelAddToContactCommand = IsAddedToCollection;
            ShowCommitRemoveFromContactCommand = IsRemovedFromCollection;
            ShowUndoRemoveFromContactCommand = IsRemovedFromCollection;
        }

        #endregion

        #region Protected

        protected override void OnIsDirtyChanged()
        {
            base.OnIsDirtyChanged();
            ConfigureCommandVisibility();
        }

        protected override void OnIsInvalidChanged()
        {
            base.OnIsInvalidChanged();
            ConfigureCommandVisibility();
        }

        protected override void OnCollectionStatusChanged(String parentCollectionPropertyName)
        {
            base.OnCollectionStatusChanged(parentCollectionPropertyName);
            
            ConfigureCommandVisibility();
            

            if (IsInCollection)
            {
                ContactViewModel parentViewModel = this.GetParent(typeof(ContactViewModel)) as ContactViewModel;
                parentViewModel.AddedAddressViewModels.Refresh();
            }
        }

        #endregion

        #region Private

        private void OnSave(Object parameter)
        {
            Repository.SaveAddress(this);

            Commit();

            if (IsAddedToCollection)
            {
                (this.GetParent(typeof(ContactViewModel)) as ContactViewModel).Addresses.Commit(this);
            }
        }

        private void OnUndoChanges(Object parameter)
        {
            Reset();
        }

        private void OnRemoveFromContact(Object parameter)
        {
            (this.GetParent(typeof(ContactViewModel)) as ContactViewModel).Addresses.Remove(this);
        }

        private void OnUndoRemoveFromContact(Object parameter)
        {
            (this.GetParent(typeof(ContactViewModel)) as ContactViewModel).Addresses.UndoRemovedViewModel(this);
        }

        private void OnRemoveFromContactCommit(Object parameter)
        {
            Repository.RemoveAddress(this);
            (this.GetParent(typeof(ContactViewModel)) as ContactViewModel).Addresses.Commit(this);
        }

        private void OnCancelAddToContact(Object parameter)
        {
            (this.GetParent(typeof(ContactViewModel)) as ContactViewModel).Addresses.Remove(this);
        }

        #endregion
    }
}
